
Imports System.IO
Imports streamhandle

Namespace streamhandle
    Public Module streamhandle
        Public Const defaultRetryTimes As Int64 = 64
        Public Const readstep As Int32 = 65536

        Public Function timeoutms(ByRef starttime As Int64,
                                  ByVal holdtime As Int64) As Int32
            If starttime = npos Then
                starttime = Now.milliseconds()
            Else
                holdtime -= Now.milliseconds() - starttime
            End If
            Return min(max(holdtime, 1), maxInt32)
        End Function

        Private Function stream_valid(ByVal s As Stream, ByVal can As _do(Of Stream, Boolean)) As Boolean
            Return Not s Is Nothing AndAlso object_compare(s, Stream.Null) <> 0 AndAlso can(s)
        End Function

        Public Function stream_canwrite(ByVal s As Stream) As Boolean
            Return stream_valid(s,
                                Function(ByRef i As Stream) As Boolean
                                    Return s.CanWrite()
                                End Function)
        End Function

        Public Function stream_canread(ByVal s As Stream) As Boolean
            Return stream_valid(s,
                                Function(ByRef i As Stream) As Boolean
                                    Return s.CanRead()
                                End Function)
        End Function

        Public Function initial_write(ByVal s As Stream,
                                      ByVal d() As Byte,
                                      ByVal offsetInBuff As Int64,
                                      ByRef length As Int64,
                                      ByRef starttime As Int64,
                                      ByRef holdtime As Int64) As ternary
            If Not stream_canwrite(s) Then
                raiseError(errorHandle.errorType.warning, "cannot write to output stream.")
                Return ternary.false
            End If

            If d Is Nothing Then
                raiseError(errorHandle.errorType.warning, "input byte array is nothing.")
                Return ternary.false
            End If

            If length = npos Then
                length = d.Length() - offsetInBuff
            End If

            If length <= 0 Then
                Return ternary.true
            End If

            If s.CanTimeout() Then
                s.WriteTimeout() = timeoutms(starttime, holdtime)
            End If

            Return ternary.unknown
        End Function

        Public Function initial_traversal(ByVal s As Stream,
                                          ByRef offsetInStream As Int64,
                                          ByRef length As Int64,
                                          ByRef starttime As Int64,
                                          ByVal holdtime As Int64) As ternary
            If Not stream_canread(s) Then
                raiseError("input stream is nothing, or cannot read.", errorHandle.errorType.warning)
                Return ternary.false
            End If

            If length < 0 AndAlso s.CanSeek() Then
                If offsetInStream = npos Then
                    length = s.Length()
                Else
                    length = s.Length() - offsetInStream
                End If
            End If
            If length < 0 Then
                length = npos
            ElseIf length = 0 Then
                Return ternary.true
            End If

            If offsetInStream <> npos Then
                If s.CanSeek() Then
                    s.Position = offsetInStream
                Else
                    raiseError("cannot set position to a stream which cannot seak.", errorHandle.errorType.exclamation)
                End If
            End If

            If s.CanTimeout() Then
                s.ReadTimeout() = timeoutms(starttime, holdtime)
            End If

            Return ternary.unknown
        End Function
    End Module
End Namespace

Public Module _streamhandle

    Public Function closeBinaryWriter(Of t As BinaryWriter)(ByRef writer As t) As Boolean
        On Error Resume Next
        If writer Is Nothing Then
            Return False
        Else
            writer.Flush()
            closestream(writer.BaseStream())
            writer.Close()

            writer = Nothing
            Return True
        End If
    End Function

    Public Function closeBinaryReader(Of t As BinaryReader)(ByRef reader As t) As Boolean
        On Error Resume Next
        If reader Is Nothing Then
            Return False
        Else
            closestream(reader.BaseStream())
            reader.Close()

            reader = Nothing
            Return True
        End If
    End Function

    Public Function closereader(Of t As TextReader)(ByRef reader As t) As Boolean
        On Error Resume Next
        If reader Is Nothing Then
            Return False
        Else
            Dim istream As Stream = Nothing
            istream = If(istype(Of StreamReader)(reader),
                         convertor.cast(Of StreamReader)(reader).BaseStream(),
                         Nothing)
            closestream(istream)
            reader.Close()
            istream = Nothing
#If Not (PocketPC OrElse Smartphone) Then
            reader.Dispose()
#End If
            reader = Nothing
            Return True
        End If
    End Function

    Public Function closewriter(Of t As TextWriter)(ByRef writer As t) As Boolean
        On Error Resume Next
        If writer Is Nothing Then
            Return False
        Else
            writer.Flush()
            Dim ostream As Stream = Nothing
            ostream = If(istype(Of StreamWriter)(writer),
                         convertor.cast(Of StreamWriter)(writer).BaseStream(),
                         Nothing)
            closestream(ostream)
            writer.Close()
            ostream = Nothing
#If Not (PocketPC OrElse Smartphone) Then
            writer.Dispose()
#End If
            writer = Nothing
            Return True
        End If
    End Function

    Public Function closestream(ByRef oper As TextWriter) As Boolean
        Return closewriter(oper)
    End Function

    Public Function closestream(ByRef oper As TextReader) As Boolean
        Return closereader(oper)
    End Function

    Public Function closestream(ByRef oper As Stream) As Boolean
        On Error Resume Next
        If Not oper Is Nothing Then
            oper.Flush()
            oper.Close()
#If Not (PocketPC OrElse Smartphone) Then
            oper.Dispose()
#End If

            oper = Nothing
            Return True
        Else
            Return False
        End If
    End Function

    Public Function closestream(ByRef oper As BinaryReader) As Boolean
        Return closeBinaryReader(oper)
    End Function

    Public Function closestream(ByRef oper As BinaryWriter) As Boolean
        Return closeBinaryWriter(oper)
    End Function

#If RETIRED Then
    Public Function closestream(Of t)(ByRef oper As t) As Boolean
        On Error Resume Next

        Dim extCode As Boolean = True
        If Not oper Is Nothing Then
            If TypeOf oper Is TextWriter Then
                Return closewriter(convertor.cast(Of TextWriter)(oper))
            ElseIf TypeOf oper Is TextReader Then
                Return closereader(convertor.cast(Of TextReader)(oper))
            ElseIf TypeOf oper Is Stream Then
                Dim s As Stream = Nothing
                s = convertor.cast(Of Stream)(oper)
                s.Flush()
                s.Close()
#If Not (PocketPC OrElse Smartphone) Then
                s.Dispose()
#End If
                extCode = True
            ElseIf TypeOf oper Is BinaryReader Then
                Return closeBinaryReader(convertor.cast(Of BinaryReader)(oper, True))
            ElseIf TypeOf oper Is BinaryWriter Then
                Return closeBinaryWriter(convertor.cast(Of BinaryWriter)(oper, True))
            Else
                extCode = False
            End If
            oper = Nothing
        Else
            extCode = False
        End If

        Return extCode
    End Function
#End If

    Public Function writestream(ByVal s As Stream, ByVal buff() As Byte, _
                                Optional ByVal offsetInBuff As Int32 = 0, _
                                Optional ByVal lengthInBuff As Int32 = npos, _
                                Optional ByVal retrytimes As Int32 = npos, _
                                Optional ByVal starttime As Int64 = npos, _
                                Optional ByVal holdtime As Int64 = maxInt64, _
                                Optional ByVal sleeptime As Int64 = npos) As Boolean
        Dim rtn As ternary = Nothing
        rtn = initial_write(s, buff, offsetInBuff, lengthInBuff, starttime, holdtime)
        If rtn.notunknown() Then
            Return rtn.true_()
        End If
        If retrytimes = npos AndAlso sleeptime > 0 Then
            retrytimes = holdtime \ sleeptime
        End If
        If retrytimes <= 0 Then
            retrytimes = defaultRetryTimes
        End If

        Dim i As Int32
        For i = 0 To retrytimes - 1
            Try
                s.Write(buff, offsetInBuff, lengthInBuff)
                Exit For
            Catch
                If holdtime <> npos Then
                    If ticksToMilliseconds(Now.Ticks) - starttime < holdtime Then
                        If sleeptime <> npos Then
                            sleep(sleeptime)
                        End If
                    Else
                        i = retrytimes
                    End If
                End If
            End Try
        Next

        Return i < retrytimes
    End Function

#If RETIRED Then
    Public Function writestream(ByVal s As Stream, ByVal buff As bytes _
                                , Optional ByVal start As Int32 = 0 _
                                , Optional ByVal length As Int32 = npos _
                                , Optional ByVal retrytimes As Int32 = npos _
                                , Optional ByVal starttime As Int64 = npos _
                                , Optional ByVal holdtime As Int64 = npos _
                                , Optional ByVal sleeptime As Int64 = npos) As Boolean
        If buff Is Nothing OrElse s Is Nothing Then
            Return False
        Else
            Return writestream(s, buff.data, start, length, retrytimes, starttime, holdtime, sleeptime)
        End If
    End Function
#End If

End Module
